/* vim: set expandtab shiftwidth=4: -*- mode: c++; c-basic-offset: 4; tab-width: 8; indent-tabs-mode: nil -*-
qRPG
An RPG written to test Qt's Graphics View framework
Copyright (C) 2006 Wesley Crossman
Email: wesley@crossmans.net

You can redistribute and/or modify this software under the terms of the GNU
General Public License as published by the Free Software Foundation;
either version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this
program; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
Suite 330, Boston, MA 02111-1307 USA */

#include "BoardEntry.h"
#include "BoardCharacter.h"
#include "GameBoardScene.h"
#include "qmath.h"
#include "itemeffects.h"
#include "DynamicArray.h"
#include <QtGui>
#include <cmath>
#include <cassert>

GameBoardScene::GameBoardScene(QObject *parent) : QGraphicsScene(parent) {
    setBackgroundBrush(QColor(20, 20, 20));
    tileSize = 32;
    ItemMover::instance(this)->getTimer()->start(60);
    me = NULL;
    border = NULL;
    statusTimer = new QTimer(this);
    statusTimer->setSingleShot(true);
    connect(statusTimer, SIGNAL(timeout()), SLOT(updateMessage()));
    QFile file("levels/test.lvl");
    assert(file.open(QFile::ReadOnly));
    QTextStream stream(&file);
    loadBoard(stream);
}

void GameBoardScene::deleteBoardEntries() {
    for (int x=0;x<array.width();++x) {
        for (int y=0;y<array.height();++y) {
            if (array.getEntry(x, y)) delete array.getEntry(x, y);
            array.setEntry(x, y, NULL);
        }
    }
}

void GameBoardScene::resetBoard() {
    physicalSize = boardSize * tileSize;
    setSceneRect(0, 0, physicalSize.width(), physicalSize.height());
    deleteBoardEntries();
    array.setSize(boardSize.width(), boardSize.height());
    for (int x=0;x<boardSize.width();++x) {
        for (int y=0;y<boardSize.height();++y) {
            array.setEntry(x, y, new BoardEntry(x*tileSize, y*tileSize, this));
        }
    }
}

void GameBoardScene::loadBoard(QTextStream &stream) {
    qDebug() << "Loading file...";
    //* settings
    boardSize = QSize();
    int layerCount = 1;
    while (!stream.atEnd()) {
        QString line = stream.readLine();
        if (line == "[layers]") break;
        if (line.startsWith("#")) continue;
        QStringList parts = line.split("=");
        if (parts[0] == "size") {
            QStringList dimensions = parts[1].split(",");
            bool ok = true;
            int width = dimensions[0].toInt(&ok);
            assert(ok);
            int height = dimensions[1].toInt(&ok);
            assert(ok);
            boardSize = QSize(width, height);
        } else if (parts[0] == "layers") {
            bool ok = true;
            layerCount = parts[1].toInt(&ok);
            assert(ok);
        }
    }
    assert(!boardSize.isNull());
    
    //* layers
    while (!stream.atEnd()) {
        QString line = stream.readLine();
        if (line == "[pieces]") break;
        if (line.startsWith("#")) continue;
        QStringList parts = line.split("=");
        layerNames[parts[1]] = parts[0].toInt() - 1;
        assert(layerNames[parts[1]] >= 0 && layerNames[parts[1]] < layerCount);
    }
    
    //create board from parameters
    qDebug() << "Preparing board...";
    resetBoard();
    delete border;
    border = new QGraphicsRectItem(-1, -1, physicalSize.width()+1, physicalSize.height()+1,
            NULL, this);
    border->setPen(QPen(Qt::black));
    for (int x=0;x<boardSize.width();++x) {
        for (int y=0;y<boardSize.height();y++) {
            array.getEntry(x, y)->prepEntry(layerCount);
        }
    }
    
    //* pieces
    qDebug() << "Loading associations...";
    mapping.clear();
    while (!stream.atEnd()) {
        QString line = stream.readLine();
        if (line == "[board]") break;
        if (line.startsWith("#")) continue;
        QStringList parts = line.split("=");
        mapping[parts[0]] = QPixmap(parts[2]);
        if (mapping[parts[0]].isNull()) {
            qDebug() << parts[2];
            assert(false);
        }
    }
    
    //* board
    qDebug() << "Loading board contents...";
    int y = 0;
    while (!stream.atEnd()) {
        QString line = stream.readLine();
        if (line == "[character]") break;
        if (line.startsWith("#")) continue;
        QStringList entries = line.split(";");
        int x = 0;
        foreach (QString entry, entries) {
            QStringList entryLayers = entry.split(",");
            for (int a=0;a<entryLayers.size();++a) {
                if (mapping.contains(entryLayers[a])) {
                    assert(array.getEntry(x, y)->items.size() > 0);
                    array.getEntry(x, y)->items[a]->setPixmap(mapping[entryLayers[a]]);
                }
            }
            ++x;
        }
        ++y;
    }

    //* character
    qDebug() << "Loading characters...";
    while (!stream.atEnd()) {
        QString line = stream.readLine();
        if (line.size() == 0) break;
        if (line.startsWith("#")) continue;
        QStringList entries = line.split("=");
        if (entries[0]=="player") {
            qDebug() << "Loading player...";
            QStringList location = entries[1].split(",");
            bool ok = false;
            int x = location[0].toInt(&ok);
            assert(ok);
            int y = location[1].toInt(&ok);
            assert(ok);
            me = new Hero(x, y, this);
            connect(me, SIGNAL(moved()), SLOT(tick()));
            connect(me, SIGNAL(contentsChanged()), SIGNAL(playerInventoryChanged()));
            QPixmap pix(entries[2]);
            assert(!pix.isNull());
            me->item->setPixmap(pix);
            QTimer::singleShot(0, this, SLOT(ensureHeroVisible()));
        } else {
            QStringList location = entries[1].split(",");
            bool ok = false;
            int x = location[0].toInt(&ok);
            assert(ok);
            int y = location[1].toInt(&ok);
            assert(ok);
            GameItem *item = NULL;
            if (entries[0] == "potion") {
                Potion *potion = new Potion(x, y, this);
                item = potion;
            } else if (entries[0] == "scroll") {
                Scroll *scroll = new Scroll(x, y, this);
                item = scroll;
            } else if (entries[0] == "bag") {
                Bag *bag = new Bag(x, y, this);
                item = bag;
            } else if (entries[0] == "enemy") {
                Enemy *enemy = new Enemy(x, y, this);
                item = enemy;
            } else {
                assert(0);
            }
            if (item) {
                QPixmap pix(entries[2]);
                assert(!pix.isNull());
                item->item->setPixmap(pix);
            }
        }
    }
    
    qDebug() << "Done.";

    emit playerMoved();
}

void GameBoardScene::ensureHeroVisible() {
    me->ensureVisible();
}

void GameBoardScene::tick() {
    foreach (GameItem *item, items) {
        item->tick();
    }
    emit playerMoved();
}

void GameBoardScene::keyPressEvent(QKeyEvent *e) {
    switch (e->key()) {
    case Qt::Key_1: case Qt::Key_2: case Qt::Key_3:
    case Qt::Key_4: case Qt::Key_5: case Qt::Key_6:
    case Qt::Key_7: case Qt::Key_8: case Qt::Key_9:
        me->keyPressed(e);
        break;
    }
    QGraphicsScene::keyPressEvent(e);
}

QList < GameItem * > GameBoardScene::getAtPosition(QPoint pos) {
    assert(pos.x() >= 0 && pos.y() >= 0
            && pos.x() < boardSize.width() && pos.y() << boardSize.height());
    QList < GameItem * > newList;
    foreach (GameItem *item, items) {
        if (pos == item->getPos() && !item->hasParent()) newList.append(item);
    }
    return newList;
}
        
QList < GameItem * > GameBoardScene::getUnderFoot() {
    QList < GameItem * > newList = getAtPosition(me->getPos());
    newList.removeAll(me);
    new Star(rand() % int(width()), rand() % int(height()), NULL, this); //DEBUG
    new Flash(rand() % int(width()), rand() % int(height()), NULL, this); //DEBUG
    return newList;
}

void GameBoardScene::changeLayerZValue(int layer, int change) {
    for (int x=0;x<boardSize.width();++x) {
        for (int y=0;y<boardSize.height();y++) {
            QGraphicsPixmapItem *item = array.getEntry(x, y)->items[layer];
			item->setZValue(item->zValue() + change);
        }
    }
}

void GameBoardScene::showMessage(const QString &message, int timeout) {
    if (!statusTimer->isActive()) {
        emit sigShowMessage(message);
        statusTimer->start(timeout);
    } else {
        statusEntries.append(QPair < QString, int >(message, timeout));
    }
}

void GameBoardScene::updateMessage() {
    if (statusEntries.size()) {
        QPair < QString, int > entry = statusEntries[0];
        statusEntries.removeFirst();
        emit sigShowMessage(entry.first);
        statusTimer->start(entry.second);
    } else {
        emit sigShowMessage("");
    }
}

void GameBoardScene::registerItem(GameItem *item) {
    items.append(item);
}

void GameBoardScene::unregisterItem(GameItem *item) {
    items.removeAll(item);
}

